#!/usr/bin/python
# -*- coding:utf-8 -*-
# @time: 2021/6/8 8:46 下午

import string
import datetime
import os
import random
from io import BytesIO
from math import ceil

try:
    import Image
    import ImageDraw
    import ImageFont
except ImportError:
    from PIL import Image, ImageDraw, ImageFont

from django.conf import settings
from django.http import HttpResponse
from wass.settings import CAPTCHA

current_path = os.path.normpath(os.path.dirname(__file__))
CAPTCHA_SESSION_KEY = getattr(settings, 'CAPTCHA_SESSION_KEY', 'CAPTCHA_SESSION_KEY')
CAPTCHA_SESSION_TIME_KEY = getattr(
    settings, 'CAPTCHA_SESSION_TIME_KEY', 'CAPTCHA_SESSION_TIME_KEY'
)


def random_string(length):
    base = string.ascii_letters + string.digits
    return ''.join([random.choice(base) for i in range(length)])


def random_digits(length):
    return ''.join([random.choice(string.digits) for _ in range(length)])


class Code(object):
    def __init__(self, request):
        """
        初始化,设置各种属性
        """
        self.django_request = request
        self.session_key = CAPTCHA_SESSION_KEY
        self.session_expire_key = CAPTCHA_SESSION_TIME_KEY
        self.expires = 300

        # 验证码图片尺寸
        self.img_width = 86
        self.img_height = 32
        self.type = 'number'

    def set_expires(self, expire):
        self.expires = expire

    def _get_font_size(self):
        """字体大小"""
        s1 = int(self.img_height * 1.2)
        s2 = int(self.img_width / len(self.code))
        return int(min((s1, s2)) + max((s1, s2)) * 0.01)

    def _get_words(self):
        """
        读取默认的单词表
        """
        _words = r'123456789abcdefhkmnrstuvwxzABCDEFGHKMNQRSTUVWXZ'
        return ''.join(random.sample(_words, 4))

    def _set_answer(self, answer):
        """设置答案"""
        self.django_request.session[self.session_key] = str(answer)
        self.django_request.session[self.session_expire_key] = datetime.datetime.now()

    def _yield_code(self):
        """生成验证码文字,以及答案"""

        # 英文单词验证码
        def world():
            code = self._get_words()
            self._set_answer(code)
            return code

        # 数字公式验证码
        def number():
            m, n = 1, 50
            x = random.randrange(m, n)
            y = random.randrange(m, n)

            r = random.randrange(0, 2)
            if r == 0:
                code = "%s - %s = ?" % (x, y)
                z = x - y
            else:
                code = "%s + %s = ?" % (x, y)
                z = x + y
            self._set_answer(z)
            return code

        fun = eval(self.type.lower())
        return fun()

    def display(self):
        """
        验证码生成
        """
        # 验证码字体颜色
        self.font_color = ['goldenrod', 'teal', 'chocolate', 'darkolivegreen', 'plum']
        # 背景颜色
        self.backgrounds = [
            'snow',
            'seashell',
            'oldlace',
            'mintcream',
            'ivory',
            'honeydew',
            'ghostwhite',
            'cornsilk',
        ]
        # 字体文件路径
        self.font_path = os.path.join(current_path, 'FreeSansBoldOblique.ttf')
        # the words list maxlength = 8
        self.django_request.session[self.session_key] = ''
        # creat a image
        im = Image.new(
            'RGB', (self.img_width, self.img_height), random.choice(self.backgrounds)
        )
        self.code = self._yield_code()
        # 更具图片大小自动调整字体大小
        self.font_size = self._get_font_size()
        # creat a pen
        draw = ImageDraw.Draw(im)

        for i in range(random.randrange(2, 3)):
            line_color = (
                random.randrange(0, 255),
                random.randrange(0, 255),
                random.randrange(0, 255),
            )
            xy = (
                random.randrange(0, int(self.img_width * 0.5)),
                random.randrange(0, self.img_height),
                random.randrange(int(3 * self.img_width / 4), self.img_width),
                random.randrange(0, self.img_height),
            )
            draw.line(xy, fill=line_color, width=int(self.font_size * 0.2))
        for i in range(random.randrange(1, 3)):
            line_color = (
                random.randrange(0, 255),
                random.randrange(0, 255),
                random.randrange(0, 255),
            )
            xy = (
                random.randrange(0, int(self.img_width)),
                random.randrange(0, self.img_height),
                random.randrange(int(3 * self.img_width / 4), self.img_width),
                random.randrange(0, self.img_height),
            )
            draw.arc(xy, 100, 200, fill=line_color)
        # 绘制干扰点
        point_chance = 2
        width, height = (96, 32)
        chance = min(50, max(0, int(point_chance)))
        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 50)
                if tmp > 50 - chance:
                    draw.point((w, h), fill='linen')

        # 写验证码
        j = int(self.font_size * 0.02)
        k = int(self.font_size * 0.3)
        x = random.randrange(j, k)  # 起始位置
        for i in self.code:
            # 上下抖动量,字数越多,上下抖动越大
            y = random.randrange(1, 3)
            if i in ('+', '=', '?'):
                # 对计算符号等特殊字符放大处理
                m = ceil(self.font_size * 0.8)
            else:
                # 字体大小变化量,字数越少,字体大小变化越多
                m = random.randrange(
                    0, int(45 / self.font_size) + int(self.font_size / 4)
                )

            self.font = ImageFont.truetype(
                self.font_path.replace('\\', '/'), self.font_size + int(ceil(m))
            )
            draw.text((x, y), i, font=self.font, fill=random.choice(self.font_color))
            x += self.font_size * 0.8

        del x
        del draw
        buf = BytesIO()
        im.save(buf, 'gif')
        response = HttpResponse(buf.getvalue(), 'image/gif')
        response['Cache-Control'] = 'no-cache'
        buf.close()
        return response

    def check(self, code):
        """
        检查用户输入的验证码是否正确
        """
        if not CAPTCHA:
            return True
        _code = self.django_request.session.get(self.session_key) or ''
        _expires_time = self.django_request.session.get(self.session_expire_key) or None
        if _expires_time:
            _expires_time = _expires_time.replace(tzinfo=None)
        if (
            _expires_time
            and _expires_time
            < datetime.datetime.now() + datetime.timedelta(seconds=self.expires)
        ):
            code = str(code).lower()
            # 清空session中的值，防止重复提交
            self.django_request.session[self.session_key] = ''
            self.django_request.session[self.session_expire_key] = ''
            return _code.lower() == code
        else:
            return False
